/**
 * 
 */
package vue.desktop;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

import model.cnf.CNF;
import model.session.PreprocessorExecutionBlock;
import model.session.SessionBlock;
import vue.IGLink;
import vue.IIGSATPart;
import vue.session.IGBlock;
import vue.session.IGCNFUse;
import vue.session.IGExecutionBlock;
import vue.session.IGPreprocessorBlock;
import vue.session.IGSolverBlock;
import controller.ISessionManager;

/**
 * {@link IGDesktop} is a part of the application that allow you to see your progression in a
 * session with a graphical representation of the {@link CNF} and {@link PreprocessorExecutionBlock}
 * .
 * 
 * @author GRONOWSKI Stephane svan001@aol.com
 * 
 *         igsat 2010
 * 
 */
public class IGDesktop extends JPanel implements IIGSATPart, MouseMotionListener {

	private static final long							serialVersionUID	= 1L;
	public static final String							BORDER_TITLE		= "Desktop";
	public static final Color							BACKGROUND_COLOR	= Color.WHITE;
	public static final Color							SHORTEST_PATH_COLOR	= Color.BLUE;
	public static final Color							PATH_COLOR			= Color.BLACK;

	public static final int								HEIGHT				= 600;
	public static final int								WIDTH				= 900;

	public static final int								PAS					= 130;
	public static final int								FIRST_X				= 70;
	public static final int								FIRST_Y				= 70;


	protected int										width				= WIDTH;
	protected int										height				= HEIGHT;
	protected Dimension									dim;

	private ISessionManager								manager;

	private Map<IGDeskCNFUse, Set<IGDeskPreprocessor>>	cnfUses				= new HashMap<IGDeskCNFUse, Set<IGDeskPreprocessor>>();
	private Map<IGDeskPreprocessor, Set<IGDeskCNFUse>>	preprocs			= new HashMap<IGDeskPreprocessor, Set<IGDeskCNFUse>>();

	private IGBlock										selected			= null;
	private IGDeskItem									itemSelected		= null;
	private Map<IGBlock, IGDeskItem>					secondSelected		= new HashMap<IGBlock, IGDeskItem>();

	private IGLink										link;

	/**
	 * Create a {@link IGDesktop}.
	 * 
	 * @param manager
	 *            the session manager
	 */
	public IGDesktop(ISessionManager manager, IGLink link) {
		setLayout(null);
		this.manager = manager;
		this.link = link;
		dim = new Dimension(WIDTH, HEIGHT);


		// setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
		// setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
		setBorder(BorderFactory.createTitledBorder(BORDER_TITLE));

		setBackground(BACKGROUND_COLOR);
		setPreferredSize(dim);
		setSize(dim);


	}

	@Override
	public void addCNFUse(IGCNFUse cnfuse) {

		// new data
		IGDeskCNFUse res = new IGDeskCNFUse(cnfuse, manager);
		res.addMouseMotionListener(this);

		// maj
		cnfUses.put(res, new HashSet<IGDeskPreprocessor>());

		add(res);

		// res.setLocation(100 * (preprocs.size() + cnfUses.size()), 100 * (preprocs.size() +
		// cnfUses.size()));
		if (cnfuse.getFirst())
			res.setLocation(FIRST_X, FIRST_Y);
		else
			res.setLocation(0, 0);

		repaint();
		validate();
	}

	@Override
	public void addExecution(IGCNFUse cnfuse, IGExecutionBlock exec) {
		// do nothing
	}

	@Override
	public void addPreprocessor(IGCNFUse cnfuse, IGPreprocessorBlock preproc, IGCNFUse res) {
		IGDeskCNFUse dep = null;
		IGDeskCNFUse arr = null;

		Set<IGDeskPreprocessor> pdep = null;
		Set<IGDeskPreprocessor> parr = null;

		for (IGDeskCNFUse cnf : cnfUses.keySet())
		{// seek the good igdeskcnfuse
			if (dep == null && cnf.getCnfuse() == cnfuse)
			{
				dep = cnf;
				pdep = cnfUses.get(dep);
				if (arr != null)
					break;
				else
					continue;
			}

			if (arr == null && cnf.getCnfuse() == res)
			{
				arr = cnf;
				parr = cnfUses.get(arr);
				if (dep != null)
					break;
				else
					continue;
			}
		}

		if (dep == null || arr == null)
			return; // cnf pas present

		// new data
		IGDeskPreprocessor prec = new IGDeskPreprocessor(preproc, manager);
		prec.addMouseMotionListener(this);
		Set<IGDeskCNFUse> setcnf = new HashSet<IGDeskCNFUse>();
		setcnf.add(dep);
		setcnf.add(arr);

		// maj
		pdep.add(prec);
		parr.add(prec);

		preprocs.put(prec, setcnf);

		// TODO coordonne

		add(prec);
		// prec.setLocation(100 * (preprocs.size() + cnfUses.size()), 100 * (preprocs.size() +
		// cnfUses.size()));
		// prec.setLocation(getWidth() / 2, getHeight() / 2);
		setItemPosition(prec, dep);
		System.out.println("coorcnf arriver");
		setItemPosition(arr, prec);


		repaint();
		validate();
	}

	private void setItemPosition(IGDeskItem item, IGDeskItem dep) {
		int x, y, decalageX, decalageY;
		boolean possible = true;

		decalageX = item.getWidth() / 2;
		decalageY = item.getHeight() / 2;

		x = dep.getX() + dep.getWidth() / 2 - decalageX;
		y = dep.getY() + dep.getHeight() / 2 - decalageY;
		int decdepX = dep.getWidth() / 2;
		int decdepY = dep.getHeight() / 2;


		for (int i = 1 ; possible ; i++)
		{// cherche bonne coor
			possible = false;

			// checke si possible a droite
			item.setLocation(x + decdepX + PAS * i, y);
			try
			{
				if (checkCoor(item))
					// coor possible
					break;
				possible = true;
			} catch (Exception e)
			{
				System.out.println("exception");
			}
			System.out.println("continue");
			// checke si possible en bas
			item.setLocation(x, y + decdepY + PAS * i);
			try
			{
				if (checkCoor(item))
					// coor possible
					break;
				possible = true;
			} catch (Exception e)
			{
			}

			// checke si possible a gauche
			item.setLocation(x - decdepX - PAS * i, y);
			try
			{
				if (checkCoor(item))
					// coor possible
					break;
				possible = true;
			} catch (Exception e)
			{
			}

			// checke si possible en haut
			item.setLocation(x, y - decdepY - PAS * i);
			try
			{
				if (checkCoor(item))
					// coor possible
					break;
				possible = true;
			} catch (Exception e)
			{
			}

			// checke si possible en haut a droite
			item.setLocation(x + PAS * i, y - PAS * i);
			try
			{
				if (checkCoor(item))
					// coor possible
					break;
				possible = true;
			} catch (Exception e)
			{
			}

			// checke si possible en bas a droite
			item.setLocation(x + PAS / 2 * i, y + PAS / 2 * i);
			try
			{
				System.out.println("x = " + x + " y = " + y + " newX = " + item.getX() + "newY = " + item.getY());
				if (checkCoor(item))
					// coor possible
					break;
				System.out.println("false");
				possible = true;
			} catch (Exception e)
			{
			}

			// checke si possible en bas a gauche
			item.setLocation(x - PAS * i, y + PAS * i);
			try
			{
				if (checkCoor(item))
					// coor possible
					break;
				possible = true;
			} catch (Exception e)
			{
			}

			// checke si possible en haut a gauche
			item.setLocation(x - PAS * i, y - PAS * i);
			try
			{
				if (checkCoor(item))
					// coor possible
					break;
				possible = true;
			} catch (Exception e)
			{
			}

			// location par defaut
			item.setLocation(getWidth() / 2, getHeight() / 2);
		}
	}

	private boolean checkCoor(IGDeskItem item) throws Exception {
		int x = item.getX();
		int y = item.getY();
		Rectangle rec = item.getBounds();
		System.out.println(item + "x = " + x + " rec x = " + rec.getX() + " width " + rec.getWidth() + "heiht " + rec.getHeight());

		if (x < 0 || x + item.getWidth() > getWidth() || y < 0 || y + item.getHeight() > getHeight())
			throw new Exception();
		for (Component c : getComponents())
			if (c != item && c.getBounds().intersects(rec))
				return false;
		return true;
	}

	@Override
	public void addSolver(IGCNFUse cnfuse, IGSolverBlock solver) {
		// do nothing
	}

	@Override
	public void hasBeenModified(IGBlock block) {

		for (IGDeskCNFUse cnf : cnfUses.keySet())
			if (cnf.getCnfuse() == block)
			{
				cnf.refresh();
				repaint();
				return;
			}

		for (IGDeskPreprocessor pre : preprocs.keySet())
			if (pre.getPreproc() == block)
			{
				pre.refresh();
				repaint();
				return;
			}

	}

	@Override
	public void hasBeenRemoved(IGBlock block) {
		if (selected == block)
		{
			selected = null;
			itemSelected = null;
			for (IGDeskItem sec : secondSelected.values())
				sec.setSecondSelected(false);
			secondSelected.clear();
		}
		else
			if (secondSelected != null)
				secondSelected.remove(block);

		IGDeskCNFUse cnfuse = null;
		for (IGDeskCNFUse cnf : cnfUses.keySet())
			if (cnf.getCnfuse() == block)

			{
				cnfuse = cnf;
				break;
			}
		if (cnfuse != null)
		{
			// ig
			remove(cnfuse);

			// maj
			Set<IGDeskPreprocessor> setpre = cnfUses.remove(cnfuse);
			for (IGDeskPreprocessor pre : setpre)
				preprocs.get(pre).remove(cnfuse);

			repaint();
			return;
		}

		IGDeskPreprocessor preproc = null;
		for (IGDeskPreprocessor pre : preprocs.keySet())
			if (pre.getPreproc() == block)
			{
				preproc = pre;
				break;
			}
		if (preproc != null)
		{
			// ig
			remove(preproc);

			// maj
			Set<IGDeskCNFUse> setcnf = preprocs.remove(preproc);
			for (IGDeskCNFUse cnf : setcnf)
				cnfUses.get(cnf).remove(preproc);

			repaint();
			return;
		}

	}

	@Override
	public void hasBeenSelected(IGBlock block) {
		if (selected == block || block == null)
			return;

		if (selected != null)
		{
			itemSelected.setSelected(false);
			selected = null;
			itemSelected = null;
		}
		for (IGDeskItem sec : secondSelected.values())
			sec.setSecondSelected(false);

		secondSelected.clear();

		Set<IGBlock> seconds = block.getSeconds();

		for (IGDeskCNFUse cnf : cnfUses.keySet())
		{// check cnf
			if (cnf.getCnfuse() == block)
			{// doot selectionne
				setComponentZOrder(cnf, 0);
				cnf.setSelected(true);
				selected = block;
				itemSelected = cnf;

				if (secondSelected.size() != seconds.size())
					continue;
				else
					return;
			}

			if (seconds.contains(cnf.getCnfuse()))
			{// ajoute au seconde selection
				// setComponentZOrder(cnf, 1 + seconds.size());
				secondSelected.put(cnf.getCnfuse(), cnf);
				cnf.setSecondSelected(true);

				if (secondSelected.size() == seconds.size() && selected != null)
					return;
			}
		}

		for (IGDeskPreprocessor pre : preprocs.keySet())
		{// check preproc
			if (pre.getPreproc() == block)
			{// doot selectionne
				setComponentZOrder(pre, 0);
				pre.setSelected(true);
				selected = block;
				itemSelected = pre;

				if (secondSelected.size() != seconds.size())
					continue;
				else
					return;
			}

			if (seconds.contains(pre.getPreproc()))
			{// ajoute au seconde selection
				secondSelected.put(pre.getPreproc(), pre);
				pre.setSecondSelected(true);

				if (secondSelected.size() == seconds.size() && selected != null)
					return;
			}
		}
	}


	@Override
	public void mouseDragged(MouseEvent e) {
		Component compo = e.getComponent();

		// new coor
		int x = compo.getX() + (e.getX() - compo.getWidth() / 2);
		int y = compo.getY() + (e.getY() - compo.getHeight() / 2);

		// check border
		if (x < 0)
			x = 0;
		else
			if (x > getSize().width - compo.getWidth())
				x = getSize().width - compo.getWidth();

		if (y < 0)
			y = 0;
		else
			if (y > getSize().height - compo.getHeight())
				y = getSize().height - compo.getHeight();

		// set location
		e.getComponent().setLocation(x, y);
		repaint();

	}

	@Override
	public void mouseMoved(MouseEvent e) {
		// do nothing
	}

	@Override
	protected void paintComponent(Graphics g) {

		super.paintComponent(g);
		// g.setColor(BACKGROUND_COLOR);
		// g.fillRect(0, 0, g.getClipBounds().width, g.getClipBounds().height);
		// g.setColor(PATH_COLOR);

		Set<IGDeskPreprocessor> shortpre = new HashSet<IGDeskPreprocessor>();
		Set<IGDeskCNFUse> shortcnf = new HashSet<IGDeskCNFUse>();

		List<SessionBlock> shortest = manager.getShortestPath();

		for (IGDeskPreprocessor pre : preprocs.keySet())
		{// affiche tout les trait
			int x = pre.getX() + pre.getSize().width / 2;
			int y = pre.getY() + pre.getSize().height / 2;

			if (shortest != null && shortest.contains(pre.getPreproc().getPreproc()))
				for (IGDeskCNFUse cnf : preprocs.get(pre))
				{// check toutes les cnf
					if (shortest.contains(cnf.getCnfuse().getCnfuse()))
						g.setColor(SHORTEST_PATH_COLOR);
					else
						g.setColor(PATH_COLOR);
					g.drawLine(x, y, cnf.getX() + cnf.getSize().width / 2, cnf.getY() + cnf.getSize().height / 2);
				}
			else
			{
				g.setColor(PATH_COLOR);
				for (IGDeskCNFUse cnf : preprocs.get(pre))
					g.drawLine(x, y, cnf.getX() + cnf.getSize().width / 2, cnf.getY() + cnf.getSize().height / 2);
			}
		}


		for (Component compo : getComponents())
		{// efface le strait dans les cnf
			g.setColor(BACKGROUND_COLOR);
			g.fillRect(compo.getX(), compo.getY(), compo.getWidth(), compo.getHeight());

		}

	}


}
